package com.liliudong.pinkyponk.api.admin.security;

import cn.hutool.core.collection.CollectionUtil;
import com.liliudong.pinkyponk.biz.sys.entity.SysRole;
import com.liliudong.pinkyponk.biz.sys.entity.SysUser;
import com.liliudong.pinkyponk.biz.sys.enums.SysRoleDataScope;
import com.liliudong.pinkyponk.biz.sys.service.SysMenuService;
import com.liliudong.pinkyponk.biz.sys.service.SysOrgService;
import com.liliudong.pinkyponk.biz.sys.service.SysRoleService;
import com.liliudong.pinkyponk.biz.sys.service.SysUserService;
import com.liliudong.pinkyponk.core.constant.SecurityConst;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * UserDetailsService
 *
 * @author liliudong
 * @date 2022-05-27 15:39
 **/
@Service
@RequiredArgsConstructor
public class UserDetailsServiceImpl implements UserDetailsService {

    private final SysUserService sysUserService;
    private final SysRoleService sysRoleService;
    private final SysMenuService sysMenuService;
    private final SysOrgService sysOrgService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        SysUser sysUser = sysUserService.getByUsername(username);
        if (Objects.isNull(sysUser)) {
            throw new UsernameNotFoundException("Failed to find user '" + username + "'");
        }
        List<SysRole> sysRoles = sysRoleService.getRolesByUserId(sysUser.getId());
        Set<GrantedAuthority> roles = new HashSet<>();
        Set<GrantedAuthority> authorities = new HashSet<>();
        Set<Integer> dataScopes = new HashSet<>();
        Set<String> orgNos = new HashSet<>();

        if (CollectionUtil.isNotEmpty(sysRoles)) {
            dataScopes = sysRoles.stream().map(sysRole -> sysRole.getDataScope().scope)
                    .collect(Collectors.toSet());

            roles = sysRoles.stream().map(
                            sysRole -> new SimpleGrantedAuthority(SecurityConst.ROLE_PREFIX + sysRole.getRoleCode()))
                    .collect(Collectors.toSet());

            List<Integer> sysRoleIds = sysRoles.stream().map(SysRole::getId).collect(Collectors.toList());
            if (dataScopes.contains(SysRoleDataScope.ALL.scope)) {
                return loadSuperUser(sysUser, roles);
            }
            List<String> authorityList = sysMenuService.getAuthoritiesByRoleIds(sysRoleIds);

            if (CollectionUtil.isNotEmpty(authorityList)) {
                authorities = authorityList.stream().map(SimpleGrantedAuthority::new)
                        .collect(Collectors.toSet());
            }
            // 查询用户组
            List<String> orgNosByRoleIds = sysOrgService.getOrgNosByRoleIds(sysRoleIds);
            orgNos.addAll(orgNosByRoleIds);
        }

        List<String> orgNosByUserId = sysOrgService.getOrgNosByUserId(sysUser.getId());
        orgNos.addAll(orgNosByUserId);

        return new UserDetailsImpl(sysUser, roles, authorities, dataScopes, orgNos);
    }

    /**
     * loadSuperUser
     *
     * @param sysUser
     * @param roles
     * @return
     */
    private UserDetails loadSuperUser(SysUser sysUser,
                                      Set<GrantedAuthority> roles) {
        Set<GrantedAuthority> authorities = new HashSet<>();
        Set<Integer> dataScopes = new HashSet<>();

        List<String> authorityList = sysMenuService.getAllAuthorities();
        List<String> allOrgNos = sysOrgService.getAllOrgNos();

        if (CollectionUtil.isNotEmpty(authorityList)) {
            authorities = authorityList.stream().map(SimpleGrantedAuthority::new)
                    .collect(Collectors.toSet());
        }
        Set<String> orgNos = new HashSet<>(allOrgNos);

        return new UserDetailsImpl(sysUser, roles, authorities, dataScopes, orgNos);
    }

}
